package com.kmxd.ams.app.system;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableMap;
import com.kmxd.ams.app.system.command.CopyDictCmd;
import com.kmxd.ams.app.system.command.InitCropCmd;
import com.kmxd.ams.client.license.IConfLicenseSvc;
import com.kmxd.ams.client.system.ISysCropSvc;
import com.kmxd.ams.client.system.ISysFondsSvc;
import com.kmxd.ams.client.system.ISysUserSvc;
import com.kmxd.ams.client.system.dto.SysCropAddDTO;
import com.kmxd.ams.client.system.dto.SysCropEditDTO;
import com.kmxd.ams.client.system.dto.SysCropQueryDTO;
import com.kmxd.ams.client.system.dto.SysFondsAddDTO;
import com.kmxd.ams.client.system.vo.SysCropInitAddVO;
import com.kmxd.ams.client.system.vo.SysCropListVO;
import com.kmxd.ams.client.system.vo.SysCropVO;
import com.kmxd.ams.core.common.constant.CacheConst;
import com.kmxd.ams.core.common.constant.Constants;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.common.query.QueryGen;
import com.kmxd.ams.core.exception.BizTipException;
import com.kmxd.ams.core.util.IdUtil;
import com.kmxd.ams.core.util.SecurityUtil;
import com.kmxd.ams.infra.license.entity.ConfLicense;
import com.kmxd.ams.infra.system.entity.SysCrop;
import com.kmxd.ams.infra.system.mapper.SysCropMapper;
import com.kmxd.ams.infra.system.mapper.SysUserMapper;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 单位信息表 服务实现类
 *
 * @author ZuoLG
 * @since 2022-07-14
 */
@Slf4j
@Service
@AllArgsConstructor
@CacheConfig(cacheNames = CacheConst.CACHE_PREFIX_SYS_CROP)
public class SysCropSvcImpl extends ServiceImpl<SysCropMapper, SysCrop> implements ISysCropSvc {

  private final InitCropCmd initCropCmd;
  private final CopyDictCmd copyDictCmd;
  private final ISysFondsSvc sysFondsSvc;
  private final ISysUserSvc sysUserSvc;
  private SysCropMapper sysCropMapper;
  private SysUserMapper sysUserMapper;

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public Page<SysCropListVO> selectPage(Page reqPage, SysCropQueryDTO req) {
    LambdaQueryWrapper<SysCrop> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.eq(ObjectUtil.isNotEmpty(req.getCode()), SysCrop::getCode, req.getCode());
    queryWrapper.eq(ObjectUtil.isNotEmpty(req.getName()), SysCrop::getName, req.getName());
    queryWrapper.and(
        wrapper ->
            wrapper
                .eq(SysCrop::getId, req.getParentId())
                .or()
                .eq(SysCrop::getParentId, req.getParentId()));
    queryWrapper.orderByAsc(SysCrop::getSort);
    Page<SysCrop> page = sysCropMapper.selectPage(reqPage, queryWrapper);
    Page<SysCropListVO> voPage = BeanUtil.copyProperties(page, Page.class);
    voPage.setRecords(BeanUtil.copyToList(page.getRecords(), SysCropListVO.class));
    return voPage;
  }

  @Override
  public List<SysCropListVO> selectList(SysCropQueryDTO req) {
    LambdaQueryWrapper<SysCrop> queryWrapper =
        QueryGen.init(new QueryWrapper<SysCrop>(), req).lambda();
    queryWrapper.orderByAsc(SysCrop::getSort);
    List<SysCrop> list = sysCropMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, SysCropListVO.class);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<SysCrop> selectListAll() {
    return this.list();
  }

  @Override
  public void checkCrop(SysCropAddDTO req) {
    if (!this.checkUniqueCropName(req.getName(), null)) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "单位名称已存在！");
    }
    if (!this.checkUniqueCropCode(req.getCode(), null)) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "单位编码已经存在！");
    }
    // 校验全宗信息
    SysFondsAddDTO fonds = new SysFondsAddDTO();
    fonds.setCode(req.getFondsCode());
    fonds.setName(req.getFondsName());
    sysFondsSvc.checkFonds(fonds);
    // 校验用户
    if (!sysUserSvc.checkUniqueAccount(req.getAdminUser(), req.getId())) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "账号已存在");
    }
  }

  @CacheEvict(allEntries = true)
  @Override
  @Transactional(rollbackFor = {Throwable.class})
  public void add(SysCropAddDTO req) {
    req.setSubCropNum(ObjectUtil.isNotEmpty(req.getSubCropNum()) ? req.getSubCropNum() : 1);
    // 限制只有顶级单位可以维护
    if (ObjectUtil.notEqual(SecurityUtil.getCropId(), Constants.DEFAULT_CROP_ID)) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "您没有当前功能的操作权限，请联系管理员！");
    }
    checkCrop(req);
    SysCrop entity = BeanUtil.copyProperties(req, SysCrop.class);
    entity.setCode(entity.getCode().trim());
    entity.setName(entity.getName().trim());
    entity.setId(IdWorker.getId());
    entity.setDivide(req.getDivide() ? 1L : 0L);
    if (Objects.isNull(req.getParentId())) {
      entity.setParentId(0L);
      entity.setPathCode(entity.getParentId().toString());
    } else {
      SysCrop parent = sysCropMapper.selectById(req.getParentId());
      entity.setPathCode(parent.getPathCode() + "," + entity.getParentId().toString());
    }

    IConfLicenseSvc confLicenseSvc = SpringUtil.getBean(IConfLicenseSvc.class);
    ConfLicense confLicense = confLicenseSvc.getLicenseInfo();
    if (ObjectUtil.isEmpty(confLicense)) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "请先进行授权后再操作单位调整！");
    }
    if (Integer.parseInt(req.getSubCropNum().toString()) > confLicense.getCompanyNum()) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "下级单位数不能超过授权数！");
    }

    if (ObjectUtil.notEqual(entity.getParentId(), 0L)
        && !this.checkUniqueCropNum(req.getSubCropNum(), entity.getParentId())) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "下级单位数不能超过上级单位数！");
    }
    sysCropMapper.insert(entity);
    // 初始化相关信息
    initCropCmd.initCrop(entity, req);
    // 复制字典相关功能
    copyDictCmd.copyDict(entity, req);
  }

  @CacheEvict(allEntries = true)
  @Override
  @Transactional(rollbackFor = {Exception.class, SQLException.class})
  public void edit(SysCropEditDTO req) {
    if (!this.checkUniqueCropName(req.getName(), req.getId())) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "单位名称已存在");
    }
    SysCrop entity = BeanUtil.copyProperties(req, SysCrop.class);
    entity.setCode(entity.getCode().trim());
    entity.setName(entity.getName().trim());
    entity.setDivide(req.getDivide() ? 1L : 0L);
    if (Objects.isNull(req.getParentId())) {
      entity.setParentId(0L);
      entity.setPathCode(entity.getParentId().toString());
    } else {
      SysCrop parent = sysCropMapper.selectById(req.getParentId());
      entity.setPathCode(parent.getPathCode() + "," + entity.getParentId().toString());
    }
    SysCrop sysCrop = sysCropMapper.selectById(req.getId());
    if (!sysCrop.getCode().equalsIgnoreCase(req.getCode())) {
      if (!this.checkUniqueCropCode(req.getCode(), req.getId())) {
        throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "单位编码已经存在！");
      }
    }

    // 更新子节点的祖级列表
    List<SysCrop> sysCropList = Lists.newArrayList();
    ISysCropSvc sysCropSvc = SpringUtil.getBean(ISysCropSvc.class);
    List<SysCrop> listAll = sysCropSvc.selectListAll();
    getFindInSet(sysCropList, listAll, req.getId());

    sysCropList.forEach(
        item -> {
          item.setPathCode(
              item.getPathCode().replaceFirst(sysCrop.getPathCode(), entity.getPathCode()));
        });
    sysCropMapper.updateById(entity);
    // 启用该单位，则启用该单位的所有上级单位
    List<String> parentList = Arrays.asList(entity.getPathCode().split(","));
    parentList.stream()
        .forEach(
            item -> {
              SysCrop sysCropTemp = new SysCrop();
              sysCropTemp.setId(Long.parseLong(item));
              sysCropMapper.updateById(sysCropTemp);
            });
  }

  /**
   * 递归查找当前及子数据
   *
   * @param datas
   * @param listAll
   * @param id
   */
  private void getFindInSet(List<SysCrop> datas, List<SysCrop> listAll, Long id) {
    listAll.forEach(
        item -> {
          if (ObjectUtil.equal(id, item.getId())) {
            datas.add(item);
          } else if (ObjectUtil.equal(id, item.getParentId())) {
            datas.add(item);
            getFindInSet(datas, listAll, item.getId());
          }
        });
  }

  @CacheEvict(allEntries = true)
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void remove(String ids) {
    if (ObjectUtil.notEqual(SecurityUtil.getCropId(), Constants.DEFAULT_CROP_ID)) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "您没有当前功能的操作权限，请联系管理员！");
    }
    Set<Long> idList = IdUtil.split(ids);
    idList.forEach(
        item -> {
          SysCrop sysCrop = sysCropMapper.selectById(item);
          if (ObjectUtil.equals(Constants.DEFAULT_CROP_ID, sysCrop.getId())) {
            throw BizTipException.instance(
                ErrorCode.VALIDATE_ERROR, String.format("单位【%s】是默认单位，无法删除", sysCrop.getName()));
          }
          SysCrop sysCropChild =
              sysCropMapper.selectOne(
                  Wrappers.lambdaQuery(SysCrop.class).eq(SysCrop::getParentId, item));
          if (Objects.nonNull(sysCropChild)) {
            throw BizTipException.instance(
                ErrorCode.VALIDATE_ERROR, String.format("单位【%s】存在下级单位，无法删除", sysCrop.getName()));
          }
        });

    this.removeBatchByIds(idList);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public SysCropVO view(Long id) {
    SysCrop entity = sysCropMapper.selectById(id);
    return BeanUtil.copyProperties(entity, SysCropVO.class);
  }

  @Override
  public SysCropInitAddVO initAdd() {
    SysCropInitAddVO initAddVO = new SysCropInitAddVO();
    ISysCropSvc sysCropSvc = SpringUtil.getBean(ISysCropSvc.class);
    List<SysCrop> crops = sysCropSvc.selectListAll();
    int fondIndex = sysFondsSvc.getAutoIndex();
    initAddVO.setCropCode(String.valueOf(Constants.DEFAULT_CROP_ID + crops.size()));
    initAddVO.setFondsName(StrUtil.format("默认全宗{}", fondIndex));
    initAddVO.setFondsCode(
        String.valueOf(Constants.DEFAULT_CROP_ID + Constants.DEFAULT_CROP_ID + crops.size()));
    initAddVO.setFondsAdmin(StrUtil.format("admin{}", fondIndex));
    return initAddVO;
  }

  /**
   * 递归查找当前用户下的单位
   *
   * @param list
   * @param cropListAll
   * @param cropId
   */
  private void getCurrentCrop(Set<SysCrop> list, List<SysCrop> cropListAll, long cropId) {
    for (SysCrop sysCrop : cropListAll) {
      if (ObjectUtil.equals(sysCrop.getParentId(), cropId)) {
        list.add(sysCrop);
        getCurrentCrop(list, cropListAll, sysCrop.getId());
      }
    }
  }

  @Override
  public List<Tree<String>> listCropTree() {
    SysCropVO sysCrop = this.view(SecurityUtil.getCropId());
    ISysCropSvc sysCropSvc = SpringUtil.getBean(ISysCropSvc.class);
    List<SysCrop> sysCropListAll = sysCropSvc.selectListAll();
    Set<SysCrop> sysCropList = new HashSet<>();
    getCurrentCrop(sysCropList, sysCropListAll, SecurityUtil.getCropId());
    SysCrop sysCurrentCrop =
        sysCropListAll.stream()
            .filter(x -> ObjectUtil.equals(x.getId(), sysCrop.getId()))
            .findFirst()
            .orElse(null);
    if (sysCurrentCrop != null) {
      sysCropList.add(sysCurrentCrop);
    }

    List treeNodeList =
        sysCropList.stream()
            .map(
                item -> {
                  TreeNode treeNode = new TreeNode();
                  treeNode.setId(item.getId().toString());
                  treeNode.setName(item.getName());
                  treeNode.setParentId(item.getParentId().toString());
                  treeNode.setWeight(item.getSort());
                  treeNode.setExtra(ImmutableMap.of("title", item.getName()));
                  return treeNode;
                })
            .collect(Collectors.toList());
    List<Tree<String>> treeList =
        TreeUtil.build(treeNodeList, String.valueOf(sysCrop.getParentId()));
    return CollectionUtils.isEmpty(treeList) ? new ArrayList() : treeList;
  }

  @Override
  public List<Tree<String>> selectCropTreeAndExcludeNode(String nodeId) {
    SysCropVO sysCrop = this.view(SecurityUtil.getCropId());
    ISysCropSvc sysCropSvc = SpringUtil.getBean(ISysCropSvc.class);
    List<SysCrop> sysCropListAll = sysCropSvc.selectListAll();
    Set<SysCrop> sysCropList = new HashSet<>();
    getCurrentCrop(sysCropList, sysCropListAll, SecurityUtil.getCropId());
    SysCrop sysCurrentCrop =
        sysCropListAll.stream()
            .filter(x -> ObjectUtil.equals(x.getId(), sysCrop.getId()))
            .findFirst()
            .orElse(null);
    if (sysCurrentCrop != null) {
      sysCropList.add(sysCurrentCrop);
    }
    if (StrUtil.isNotBlank(nodeId)) {
      Iterator<SysCrop> it = sysCropList.iterator();
      while (it.hasNext()) {
        SysCrop sysCropBO = it.next();
        if (sysCropBO.getId().toString().equals(nodeId)
            || Arrays.asList(sysCropBO.getPathCode().split(",")).contains(nodeId)) {
          it.remove();
        }
      }
    }
    List treeNodeList =
        sysCropList.stream()
            .map(
                item -> {
                  TreeNode treeNode = new TreeNode();
                  treeNode.setId(item.getId().toString());
                  treeNode.setName(item.getName());
                  treeNode.setParentId(item.getParentId().toString());
                  treeNode.setWeight(item.getSort());
                  treeNode.setExtra(ImmutableMap.of("title", item.getName()));
                  return treeNode;
                })
            .collect(Collectors.toList());
    List<Tree<String>> treeList =
        TreeUtil.build(treeNodeList, String.valueOf(sysCrop.getParentId()));
    return CollectionUtils.isEmpty(treeList) ? new ArrayList() : treeList;
  }

  @Override
  public List<Tree<String>> selectCropTreeAll() {
    ISysCropSvc sysCropSvc = SpringUtil.getBean(ISysCropSvc.class);
    List<SysCrop> sysCropList = sysCropSvc.selectListAll();
    List treeNodeList =
        sysCropList.stream()
            .map(
                item -> {
                  TreeNode treeNode = new TreeNode();
                  treeNode.setId(item.getId().toString());
                  treeNode.setName(item.getName());
                  treeNode.setParentId(item.getParentId().toString());
                  treeNode.setWeight(item.getSort());
                  treeNode.setExtra(ImmutableMap.of("title", item.getName()));
                  return treeNode;
                })
            .collect(Collectors.toList());
    List<Tree<String>> treeList = TreeUtil.build(treeNodeList, "0");
    return CollectionUtils.isEmpty(treeList) ? new ArrayList() : treeList;
  }

  @Override
  public List<Tree<String>> selectCropTree() {
    SysCropVO sysCrop = this.view(SecurityUtil.getCropId());
    ISysCropSvc sysCropSvc = SpringUtil.getBean(ISysCropSvc.class);
    List<SysCrop> sysCropListAll = sysCropSvc.selectListAll();
    Set<SysCrop> sysCropList = new HashSet<>();
    getCurrentCrop(sysCropList, sysCropListAll, SecurityUtil.getCropId());
    SysCrop sysCurrentCrop =
        sysCropListAll.stream()
            .filter(x -> ObjectUtil.equals(x.getId(), sysCrop.getId()))
            .findFirst()
            .orElse(null);
    if (sysCurrentCrop != null) {
      sysCropList.add(sysCurrentCrop);
    }
    List treeNodeList =
        sysCropList.stream()
            .map(
                item -> {
                  TreeNode treeNode = new TreeNode();
                  treeNode.setId(item.getId().toString());
                  treeNode.setName(item.getName());
                  treeNode.setParentId(item.getParentId().toString());
                  treeNode.setWeight(item.getSort());
                  treeNode.setExtra(ImmutableMap.of("title", item.getName()));
                  return treeNode;
                })
            .collect(Collectors.toList());
    List<Tree<String>> treeList =
        TreeUtil.build(treeNodeList, String.valueOf(sysCrop.getParentId()));
    return CollectionUtils.isEmpty(treeList) ? new ArrayList() : treeList;
  }
  /**
   * 判断单位名称
   *
   * @param value
   * @param id
   * @return
   */
  private Boolean checkUniqueCropName(String value, Long id) {
    if (StrUtil.isBlank(value)) {
      return true;
    }
    id = Objects.isNull(id) ? -1L : id;
    SysCrop entity =
        sysCropMapper.selectOne(Wrappers.lambdaQuery(SysCrop.class).eq(SysCrop::getName, value));
    return Objects.isNull(entity) || NumberUtil.equals(entity.getId(), id);
  }

  /**
   * 判断单位编码
   *
   * @param value
   * @param id
   * @return
   */
  private Boolean checkUniqueCropCode(String value, Long id) {
    if (StrUtil.isBlank(value)) {
      return true;
    }
    id = Objects.isNull(id) ? -1L : id;
    SysCrop entity =
        sysCropMapper.selectOne(Wrappers.lambdaQuery(SysCrop.class).eq(SysCrop::getCode, value));
    return Objects.isNull(entity) || NumberUtil.equals(entity.getId(), id);
  }

  /**
   * 判断单位数
   *
   * @param value
   * @param id
   * @return
   */
  private Boolean checkUniqueCropNum(long value, Long id) {
    try {
      if (value == 0) {
        return true;
      }
      id = Objects.isNull(id) ? -1L : id;
      SysCropVO entity = this.view(id);
      return value <= entity.getSubCropNum();
    } catch (Exception e) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "下级单位数不能超过上级单位数！");
    }
  }

  @Override
  public List<SysCrop> findAllChildren(Long parentId) {
    ISysCropSvc sysCropSvc = SpringUtil.getBean(ISysCropSvc.class);
    List<SysCrop> sysCropList = sysCropSvc.selectListAll();
    SysCrop sysCrop = sysCropMapper.selectById(parentId);
    Map<Long, List<SysCrop>> group =
        sysCropList.stream().collect(Collectors.groupingBy(SysCrop::getParentId));
    List<SysCrop> sysCrops = buildSubList(group, sysCrop);
    return sysCrops;
  }

  public List<SysCrop> buildSubList(Map<Long, List<SysCrop>> group, SysCrop parentNode) {
    List<SysCrop> list = com.google.common.collect.Lists.newArrayList();
    try {
      List<SysCrop> sysCrops = group.get(parentNode.getId());
      if (ObjectUtil.isEmpty(sysCrops)) {
        return list;
      }
      list.addAll(sysCrops);
      List<SysCrop> subList =
          sysCrops.stream()
              .map(
                  crop -> {
                    List<SysCrop> cropList = group.get(crop.getId());
                    if (ObjectUtil.isNotEmpty(cropList)) {
                      List<SysCrop> childrenList =
                          cropList.stream()
                              .map(
                                  sub -> {
                                    List<SysCrop> children = buildSubList(group, sub);
                                    return children;
                                  })
                              .flatMap(Collection::stream)
                              .collect(Collectors.toList());
                      cropList.addAll(childrenList);
                    } else {
                      cropList = Lists.newArrayList();
                    }
                    return cropList;
                  })
              .flatMap(Collection::stream)
              .collect(Collectors.toList());
      list.addAll(subList);
    } catch (Exception e) {
      e.printStackTrace();
      log.error("获取父级单位列表失败！");
    }
    return list;
  }

  @Override
  public List<SysCropListVO> findAllParent(Long id) {
    ISysCropSvc sysCropSvc = SpringUtil.getBean(ISysCropSvc.class);
    List<SysCrop> sysCropList = sysCropSvc.selectListAll();
    SysCrop sysCrop = sysCropMapper.selectById(id);
    Map<Long, List<SysCrop>> group =
        sysCropList.stream().collect(Collectors.groupingBy(SysCrop::getId));
    List<SysCrop> sysCrops = buildParentList(group, sysCrop);
    return BeanUtil.copyToList(sysCrops, SysCropListVO.class);
  }

  public List<SysCrop> buildParentList(Map<Long, List<SysCrop>> group, SysCrop childNode) {
    List<SysCrop> sysCrops = group.get(childNode.getParentId());
    List<SysCrop> list = Lists.newArrayList();
    if (ObjectUtil.isEmpty(sysCrops)) {
      return list;
    }
    list.addAll(sysCrops);
    List<SysCrop> subList =
        sysCrops.stream()
            .map(
                crop -> {
                  List<SysCrop> cropList = group.get(crop.getParentId());
                  if (ObjectUtil.isNotEmpty(cropList)) {
                    List<SysCrop> parentList =
                        cropList.stream()
                            .map(
                                parent -> {
                                  List<SysCrop> superParent = buildParentList(group, parent);
                                  return superParent;
                                })
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList());
                    cropList.addAll(parentList);
                  } else {
                    cropList = Lists.newArrayList();
                  }
                  return cropList;
                })
            .flatMap(Collection::stream)
            .collect(Collectors.toList());
    list.addAll(subList);
    return list;
  }

  @CacheEvict(allEntries = true)
  @Override
  public void clearAllCache() {
    log.info("清除系统参数缓存");
  }
}
